package com.mjlf.mjatis.sql;

import com.mjlf.mjatis.annotation.Param;
import com.mjlf.mjatis.exception.BoundException;

import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * @ClassName SqlParse
 * @Author mjlft
 * @Date 2020/6/12 22:22
 * @Version 1.0
 * @Description sql解析， 将mapper类型的sql转为jdbc能认识的sql
 */
public class SqlParse {

    /**
     * 解析sql,进行参数绑定
     *
     * @param sql
     * @param args
     * @return
     */
    public static SqlBound parseSql(String sql, Object[] args, Parameter[] parameters) {
        Map<String, Object> p = mapParams(args, parameters);
        SqlBound sqlBound = parseParams(sql, p);
        return sqlBound;
    }


    /**
     * 根据mapper Sql 进行参数适配， 拿到最后的参数最后用在statement中
     *
     * @param sql
     * @return
     */
    public static SqlBound parseParams(String sql, Map<String, Object> params) {
        StringBuffer resultSql = new StringBuffer();
        SqlBound sqlBound = new SqlBound();
        if (sql != null) {
            List<Object> res = new ArrayList<>();
            for (int i = 0; i < sql.length(); i++) {
                if (('#' == sql.charAt(i) || '$' == sql.charAt(i)) && '{' == sql.charAt(i + 1)) {

                    if (params == null) {
                        throw new BoundException("未提供参数");
                    }

                    boolean replace = false;
                    if ('$' == sql.charAt(i)) {
                        replace = true;
                    }
                    StringBuffer p = new StringBuffer();
                    boolean normal = false;
                    for (int j = i + 2; j < sql.length(); j++) {
                        if ('}' != sql.charAt(j)) {
                            p.append(sql.charAt(j));
                        } else {
                            //当前参数截取结束
                            normal = true;
                            i = j;
                            break;
                        }
                    }
                    if (!normal) {
                        //sql异常结束
                        throw new BoundException("sql不合法 " + sql);
                    }
                    //首先拿到参数
                    Object value = null;
                    if (params.containsKey(p.toString())) {
                        value = params.get(p.toString());
                        if (replace) {
                            //表示需要更换
                            resultSql.append(value);
                        } else {
                            resultSql.append("?");
                            res.add(value);
                        }
                    } else {
                        throw new BoundException("参数错误, 找不到参数：" + p.toString());
                    }
                } else {
                    resultSql.append(sql.charAt(i));
                }
            }
            sqlBound.setSql(resultSql.toString());
            sqlBound.setParams(res);
        }
        return sqlBound;
    }

    /**
     * 将参数对象数组扁平化， 转换为Map对象
     * 基本数据类型需要添加注解@Param参会被认为是参数
     *
     * @param args
     * @return
     */
    public static Map<String, Object> mapParams(Object[] args, Parameter[] parameters) {

        if (args != null) {
            final Map<String, Object> params = new HashMap<>();
            for(int i = 0; i < args.length;i ++){
                Object obj = args[i];
                Parameter parameter = parameters[i];

                Param param = parameter.getDeclaredAnnotation(Param.class);
                if (param != null) {
                    //表示直接设置为参数，并且这些参数只能是基本数据类型和集合，集合中的数据也只能是基本数据类型或者是string
                    if (isBaseType(obj)) {
                        String paramName = param.value();
                        params.put(paramName, obj);
                    } else if (obj.getClass().isAssignableFrom(Collection.class)) {
                        //判断是否为集合类型,待完成
                    }
                } else {

                    //判断是否为集合
                    if (obj.getClass().isAssignableFrom(Collection.class)) {
                        //集合参数处理
                        obj.getClass().getGenericSuperclass();
                    }
                    //判断是否为Map类型
                    else if (obj.getClass().isAssignableFrom(Map.class)) {
                        //判断Key Value是否合法
                    }
                    //说明是一个对象，拿取内部基本数据类型属性
                    else {
                        Field[] fields = obj.getClass().getDeclaredFields();
                        for (Field field : fields) {
                            String str = field.getName();
                            if (isBaseType(field.getType())) {
                                try {
                                    field.setAccessible(true);
                                    Object val = field.get(obj);
                                    params.put(str, val);
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                }
                            } else {
                                //非基本类型处理
                            }
                        }
                    }
                }
            }

            return params;
        }
        return null;
    }

    /**
     * 判断是否为基本数据类型
     *
     * @param obj
     * @return
     */
    public static boolean isBaseType(Object obj) {
        return isBaseType(obj.getClass());
    }

    public static boolean isBaseType(Class type) {
        return type == int.class || type == Integer.class
                || type == byte.class || type == Byte.class
                || type == short.class || type == Short.class
                || type == long.class || type == Long.class
                || type == float.class || type == Float.class
                || type == Double.class || type == Double.class
                || type == String.class;
    }
}
